package com.bright.hxj.cwduijie.manager.impl;

import com.bright.ghj.common.util.DateUtil;
import com.bright.ghj.common.util.PageUtil;
import com.bright.ghj.common.util.StringUtil;
import com.bright.hxj.cwduijie.constant.SystemConstants;
import com.bright.hxj.cwduijie.converter.DuijieAsyncConverter;
import com.bright.hxj.cwduijie.converter.DuijieBatchConverter;
import com.bright.hxj.cwduijie.converter.DuijieDataKeysConverter;
import com.bright.hxj.cwduijie.converter.DuijieIdTaskidConverter;
import com.bright.hxj.cwduijie.enums.DataApi;
import com.bright.hxj.cwduijie.enums.SJJHBZ;
import com.bright.hxj.cwduijie.manager.DuijieManager;
import com.bright.hxj.cwduijie.pojo.bo.ProcInfo;
import com.bright.hxj.cwduijie.pojo.command.*;
import com.bright.hxj.cwduijie.pojo.dto.*;
import com.bright.hxj.cwduijie.pojo.po.*;
import com.bright.hxj.cwduijie.pojo.query.BatchQuery;
import com.bright.hxj.cwduijie.pojo.query.PageQuery;
import com.bright.hxj.cwduijie.repository.*;
import com.bright.hxj.cwduijie.util.CommonUtil;
import com.bright.hxj.cwduijie.util.EntityManagerUtil;
import com.bright.ghj.common.util.ListUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.criteria.Predicate;
import java.time.LocalDate;
import java.time.YearMonth;
import java.util.*;

/**
 * @Author hxj
 * @Date 2024/7/8 18:06
 * @Description
 */

@Component
@RequiredArgsConstructor
@Slf4j
public class DuijieManagerImpl implements DuijieManager {

    private final EntityManager entityManager;
//    private final DuijieZbRepository duijieZbRepository;
    private final DuijieAsyncRepository duijieAsyncRepository;

    private final DuijieBatchRepository duijieBatchRepository;
    private final DuijieDataKeysRepository duijieDataKeysRepository;
    private final DuijieIdTaskidRepository duijieIdTaskidRepository;
    private final DuijieCheckRepository duijieCheckRepository;

    private final DuijieVarRepository duijieVarRepository;

    private final DuijieAsyncConverter duijieAsyncConverter;
    private final DuijieBatchConverter duijieBatchConverter;

    private final DuijieDataKeysConverter duijieDataKeysConverter;
    private final DuijieIdTaskidConverter duijieIdTaskidConverter;

    private final DuijieProcLogRepository procLogRepository;

//    @Override
//    public List<DuijieZbDto> getNeedDuijieData() {
//
//        List<DuijieZb> duijieZbBySjjhbz = duijieZbRepository.getDuijieZbBySjjhbz(SJJHBZ.WAITING.getValue());
//        // TODO
//        return null;
//    }

//    @Override
//    public List<DuijieZbDto> getNeedDuijieFileData() {
//        return null;
//    }

    @Override
//    @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED)
    public List<DuijieAsyncDto> getNeedCheckAsyncTasksData() {
//        List<DuijieAsync> duijieAsyncs = duijieAsyncRepository.getBySjjhbz(SJJHBZ.EXECUTING.getValue());
//        // 20240718 加上一个新状态
//        duijieAsyncs.addAll(duijieAsyncRepository.getBySjjhbz(SJJHBZ.FEEDBACK_BUT_NOT_CHECK_COMPLETE.getValue()));

        // 20240806 查询语句加上with(nolock) 去掉每次查询都获取content字段
        List<DuijieAsync> duijieAsyncs = getDuijieAsyncsBySubSql(" and sjjhbz in ("
                + SJJHBZ.EXECUTING.getValue() + ", " + SJJHBZ.FEEDBACK_BUT_NOT_CHECK_COMPLETE.getValue() + ")", Collections.emptyMap());
        return duijieAsyncConverter.poConvertDTO(duijieAsyncs);
    }

    /**
     * 通过entityManager 语句加上with(nolock)来查询duijie_async避免死锁
     * 注意：这个方法返回的对象没有content字段的值！要content字段只能通过getDuijieAsyncById方法按id获取！
     * @param subSql
     * @return
     */
    private List<DuijieAsync> getDuijieAsyncsBySubSql(String subSql, Map<String, Object> params) {
        String querySQL = "select id as id,methodNo as methodNo,type as \"type\",taskId as taskId,sjjhbz as sjjhbz,writeLog as writeLog,createDate as createDate," +
                "writeDate as writeDate,batchId as batchId,taskState as taskState,acc_set as accSet from DUIJIE_ASYNC with(nolock) where 1=1 " + subSql + " order by createDate";

        return EntityManagerUtil.queryForClass(entityManager, querySQL, params, DuijieAsync.class);
    }

    @Override
    public Optional<DuijieAsyncDto> getDuijieAsyncById(Integer id) {
        Optional<DuijieAsync> duijieAsync = duijieAsyncRepository.findById(id);
        return duijieAsync.map(duijieAsyncConverter::poConvertDTO);
    }

//    public List<DuijieIdTaskidDto> getNeedSaveSjjhbzToDataTableData() {
        // 这里是要查duijie_id_task表中要存进各中间表的数据
//        List<DuijieIdTaskid> all = duijieIdTaskidRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
//            List<Predicate> ps = new ArrayList<>();
//            ps.add(criteriaBuilder.or(
//                    criteriaBuilder.equal(root.get("sjjhbz"), SJJHBZ.WAITING.getValue()),
//                    criteriaBuilder.equal(root.get("sjjhbz"), SJJHBZ.ERROR.getValue())));
//
//            criteriaQuery.where(ps.toArray(new Predicate[0]));
//            criteriaQuery.orderBy(criteriaBuilder.asc(root.get("id")));
//            return criteriaQuery.getRestriction();
//        });
//        return duijieIdTaskidConverter.poConvertDTO(all);
//    }

    @Override
    public void saveDuijieIdTaskId(List<DuijieIdTaskidDto> duijieIdTaskidDtos) {
        List<DuijieIdTaskid> duijieIdTaskids = duijieIdTaskidConverter.dtoConvertPO(duijieIdTaskidDtos);
        duijieIdTaskidRepository.saveAll(duijieIdTaskids);
    }

    @Override
    @Transactional
    public void saveSjjhbzToDataTable() {
        List<DuijieIdTaskid> all = duijieIdTaskidRepository.findAll(getDuijieIdTaskidSpecification());
        List<DuijieIdTaskidDto> duijieIdTaskidDtos = duijieIdTaskidConverter.poConvertDTO(all);


        // 这里是要将duijie_id_task表中要存进各中间表的数据存进各中间表
        String updateSqlTemplate = "update ${tableName} set sjjhbz=:sjjhbz, taskId=:taskId where id=:id";

        for (DuijieIdTaskidDto duijieIdTaskidDto : duijieIdTaskidDtos) {
            String serviceCode = duijieIdTaskidDto.getMethodNo();
            Optional<DataApi> optionalDataApi = DataApi.ofOptional(serviceCode);
            if (!optionalDataApi.isPresent()) {
                continue;
            }
            DataApi dataApi = optionalDataApi.get();
            try {
                log.debug("开始写入：" + duijieIdTaskidDto);
                String tableName = dataApi.getTableName();
                String updateSql = StringUtil.replace(updateSqlTemplate, "${tableName}", tableName);

                Map<String, Object> params = new HashMap<>();
                params.put("sjjhbz", duijieIdTaskidDto.getTaskSjjhbz());
                params.put("taskId", duijieIdTaskidDto.getTaskId());


                String dataIdString = duijieIdTaskidDto.getDataId();
                String[] split = StringUtil.split(dataIdString, ",");
                for (String id : split) {
                    params.put("id", id);
                    EntityManagerUtil.update(entityManager, updateSql, params);
                }
                duijieIdTaskidDto.setSjjhbz(SJJHBZ.SUCCESS.getValue());
                duijieIdTaskidDto.setWriteLog("已写入");
            } catch (Throwable throwable) {
                duijieIdTaskidDto.setSjjhbz(SJJHBZ.ERROR.getValue());
                duijieIdTaskidDto.setWriteLog("写入错误：" + throwable.getMessage());
                log.error("扫描Duijie_id_taskId 写入错误：", throwable);
            }
            duijieIdTaskidDto.setWriteDate(new Date());
        }

        // 把duijie_id_taskid的数据保存回去
        if (duijieIdTaskidDtos.size() > 0) {
            List<DuijieIdTaskid> duijieIdTaskids = duijieIdTaskidConverter.dtoConvertPO(duijieIdTaskidDtos);
            duijieIdTaskidRepository.saveAll(duijieIdTaskids);
        }
    }

    private Specification<DuijieIdTaskid> getDuijieIdTaskidSpecification() {
        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> ps = new ArrayList<>();
            Date oneDayAgo = DateUtil.addDate(new Date(), Calendar.DATE, -1);
            ps.add(criteriaBuilder.or(
                    criteriaBuilder.equal(root.get("sjjhbz"), SJJHBZ.WAITING.getValue()),
                    (criteriaBuilder.and(
                            criteriaBuilder.equal(root.get("sjjhbz"), SJJHBZ.ERROR.getValue()),
                            criteriaBuilder.lessThan(root.get("writeDate"), oneDayAgo)))));

            criteriaQuery.where(ps.toArray(new Predicate[0]));
            criteriaQuery.orderBy(criteriaBuilder.asc(root.get("id")));
            return criteriaQuery.getRestriction();
        };
    }

//    @Override
//    public void saveSendDataResult(SaveSendDataResultCommand saveSendDataResultCommand) {
//        // 20240722 由于事务写法在多线程情况下 太多线程会报事务获取失败 改成不用事务的写法 先存进Duijie_ID_TASKID 再通过定时任务写进去各张DJ_xx中间表
//        DataApi dataApi = saveSendDataResultCommand.getDataApi();
////        String tableName = dataApi.getTableName();
////        String updateSql = "update " + tableName + " set sjjhbz=:sjjhbz, taskId=:taskId where id in (:ids)";
////        Map<String, Object> params = new HashMap<>();
////        params.put("sjjhbz", saveSendDataResultCommand.getSjjhbz());
////        params.put("taskId", saveSendDataResultCommand.getTaskId());
////
////        // 一千个一次
//        List<Integer> ids = saveSendDataResultCommand.getIds();
////        int count = 0;
////        while (count < ids.size()) {
////            List<Integer> subList = ListUtil.getSubList(ids, count, count + 1000);
////            params.put("ids", subList);
////            EntityManagerUtil.update(entityManager, updateSql, params);
////            count += 1000;
////        }
//        if (ids.size() > 0) {
//            int sjjhbz = saveSendDataResultCommand.getSjjhbz();
//            String taskId = saveSendDataResultCommand.getTaskId();
//            // 存进中间表
////        List<DuijieIdTaskid> duijieIdTaskids = new ArrayList<>();
////        for (Integer id : ids) {
//            DuijieIdTaskid duijieIdTaskid = new DuijieIdTaskid();
//            duijieIdTaskid.setDataId(StringUtil.join(ids, ","));
//            duijieIdTaskid.setTaskId(taskId);
//            duijieIdTaskid.setTaskSjjhbz(sjjhbz);
//            duijieIdTaskid.setMethodNo(dataApi.getServiceCode());
//            duijieIdTaskid.setSjjhbz(SJJHBZ.WAITING.getValue());
//            duijieIdTaskid.setCreateDate(new Date());
////            duijieIdTaskids.add(duijieIdTaskid);
////        duijieIdTaskidRepository.saveAll(duijieIdTaskids);
//            duijieIdTaskidRepository.save(duijieIdTaskid);
//        }
//    }

    @Override
    @Transactional
    public void saveSendDataResult(SaveSendDataResultCommand saveSendDataResultCommand) {
        // 这里用于刚对接完返回taskId之后 回写到各张DJ_xx中间表
        DataApi dataApi = saveSendDataResultCommand.getDataApi();
        String tableName = dataApi.getTableName();
        String updateSql = "update " + tableName + " set sjjhbz=:sjjhbz, taskId=:taskId where id in (:ids)";
        Map<String, Object> params = new HashMap<>();
        params.put("sjjhbz", saveSendDataResultCommand.getSjjhbz());
        params.put("taskId", saveSendDataResultCommand.getTaskId());
//
//        // 一千个一次
        List<Integer> ids = saveSendDataResultCommand.getIds();
        int count = 0;
        while (count < ids.size()) {
            List<Integer> subList = ListUtil.getSubList(ids, count, count + 1000);
            params.put("ids", subList);
            EntityManagerUtil.update(entityManager, updateSql, params);
            count += 1000;
        }
    }

//    @Override
//    @Transactional
//    public void saveSendDataResult(List<SaveSendDataResultCommand> saveSendDataResultCommand) {
//        for (SaveSendDataResultCommand command : saveSendDataResultCommand) {
//            saveSendDataResult(command);
//        }
//    }

    @Override
    public Long getNeedSendDataResultStatusCount(String tableName) {
        try {
            return EntityManagerUtil.queryForCount(entityManager,
                    "select count(*) from \"" + tableName + "\" a, \"DUIJIE_ASYNC\" b with(nolock) where a.taskId=b.taskId and a.sjjhbz<>b.sjjhbz and a.sjjhbz in (3,4)", Collections.emptyMap());
        } catch (Throwable throwable) {
            log.debug("查询是否需要回写执行出错，跳过：" + throwable.getMessage());
            return 0L;
        }
    }

    @Override
    @Transactional
    public void updateOpTypeAndSjjhbzByIds(UpdateOpTypeSjjhbzCommand command, DataApi dataApi) {

        String tableName = dataApi.getTableName();

        String updateSql = "update " + tableName + " set sjjhbz=:sjjhbz, opType=:opType where id in (:ids)";
        Map<String, Object> params = new HashMap<>();
        params.put("sjjhbz", command.getSjjhbz());
        params.put("opType", command.getOpType());
        params.put("ids", command.getIds());

        try {
            EntityManagerUtil.update(entityManager, updateSql, params);
        } catch (Throwable throwable) {
            log.error("", throwable);
        }
    }

    @Override
    @Transactional
    public void executeUpdateSql(ExecuteUpdateSqlCommand command) {
//        try {
        EntityManagerUtil.update(entityManager, command.getSql(), command.getParams());
//        } catch (Throwable throwable) {
//            log.error("", throwable);
//        }
    }

    @Override
    @Transactional
    public void updateSendDataResultStatus(SaveSendDataResultCommand sendDataResultCommand) {
        // 通过taskId来批量更新
//        for (SaveSendDataResultCommand sendDataResultCommand : saveSendDataResultCommand) {
            DataApi dataApi = sendDataResultCommand.getDataApi();
            String tableName = dataApi.getTableName();
//            String updateSql = "update " + tableName + " set sjjhbz=:sjjhbz where taskId=:taskId";
//            Map<String, Object> params = new HashMap<>();
//            params.put("sjjhbz", sendDataResultCommand.getSjjhbz());
//            params.put("taskId", sendDataResultCommand.getTaskId());
        // 20240806 改成全表更新
            String updateSql = "update " + tableName + " set sjjhbz=b.sjjhbz from DUIJIE_ASYNC b where " + tableName + ".taskId=b.taskId and " + tableName + ".sjjhbz<>b.sjjhbz and " + tableName + ".sjjhbz in (3,4)";
            Map<String, Object> params = Collections.emptyMap();
            try {
                EntityManagerUtil.update(entityManager, updateSql, params);
            } catch (Throwable throwable) {
                log.error("", throwable);
            }
//        }
    }

    @Override
    public void saveAsyncTaskId(SaveTaskIdCommand saveTaskIdCommand) {
//        Optional<DuijieAsync> optional = duijieAsyncRepository.getDuijieAsyncByMethodNoAndTaskId(
//                saveTaskIdCommand.getServiceCode(), saveTaskIdCommand.getTaskId());
        //        duijieAsync = optional.orElseGet(DuijieAsync::new);
        DuijieAsync duijieAsync = getDuijieAsyncBySaveTaskIdCommand(saveTaskIdCommand);
        duijieAsyncRepository.save(duijieAsync);
    }

    private DuijieAsync getDuijieAsyncBySaveTaskIdCommand(SaveTaskIdCommand saveTaskIdCommand) {
        DuijieAsync duijieAsync = new DuijieAsync();
        duijieAsync.setSjjhbz(saveTaskIdCommand.getSjjhbz());
        duijieAsync.setType(saveTaskIdCommand.getType());
        duijieAsync.setTaskId(saveTaskIdCommand.getTaskId());
        duijieAsync.setWriteLog(saveTaskIdCommand.getErrorMsg());
        duijieAsync.setMethodNo(saveTaskIdCommand.getServiceCode());
        duijieAsync.setCreateDate(new Date());
        duijieAsync.setContent(saveTaskIdCommand.getContent());
        duijieAsync.setBatchId(saveTaskIdCommand.getBatchId());
        return duijieAsync;
    }

    @Override
    public void saveAsyncTaskId(List<SaveTaskIdCommand> saveTaskIdCommand) {
        List<DuijieAsync> duijieAsyncs = ListUtil.toSingleList(saveTaskIdCommand, this::getDuijieAsyncBySaveTaskIdCommand);
        duijieAsyncRepository.saveAll(duijieAsyncs);
    }

    @Override
    @Transactional
    public void updateAsyncTaskStatus(SaveTaskIdCommand saveTaskIdCommand) {


//        List<String> taskIds = ListUtil.toSingleList(saveTaskIdCommands, SaveTaskIdCommand::getTaskId);

        // 20240806 直接用taskId更新会锁全表 改成先查出id 再根据id更新
        List<DuijieAsync> duijieAsyncDtos = getDuijieAsyncsBySubSql(" and taskId=:taskId",
                Collections.singletonMap("taskId", saveTaskIdCommand.getTaskId()));
        if (duijieAsyncDtos.size() > 0) {
            // 20240729 改成用update语句
            String updateSql = "update duijie_async set sjjhbz=:sjjhbz, writeLog=:writeLog, taskState=:taskState, writeDate=GETDATE() where id=:id";
////        for (SaveTaskIdCommand saveTaskIdCommand : saveTaskIdCommands) {
            Map<String, Object> params = new HashMap<>();
            params.put("sjjhbz", saveTaskIdCommand.getSjjhbz());
            params.put("id", duijieAsyncDtos.get(0).getId());
            params.put("writeLog", saveTaskIdCommand.getErrorMsg());
            params.put("taskState", saveTaskIdCommand.getTaskState());


            EntityManagerUtil.update(entityManager, updateSql, params);
////        }
        }



        // 先获取异步任务表的数据对象
//        List<DuijieAsync> duijieAsyncList = duijieAsyncRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
//            List<Predicate> ps = new ArrayList<>();
//            ps.add(root.get("taskId").in(taskIds));
//
//            criteriaQuery.where(ps.toArray(new Predicate[0]));
////            criteriaQuery.orderBy(criteriaBuilder.asc(root.get("id")));
//            return criteriaQuery.getRestriction();
//        });


//        List<DuijieAsync> duijieAsyncList = new ArrayList<>(saveTaskIdCommands.size());

        // 在传进来要更新的数据中匹配任务ID 更新数据
//        for (DuijieAsync duijieAsync : duijieAsyncList) {
//            String taskId = duijieAsync.getTaskId();
//
//            int index = taskIds.indexOf(taskId);
//            if (index != -1) {
//                SaveTaskIdCommand saveTaskIdCommand = saveTaskIdCommands.get(index);
//                duijieAsync.setSjjhbz(saveTaskIdCommand.getSjjhbz());
//                duijieAsync.setTaskId(saveTaskIdCommand.getTaskId());
//                duijieAsync.setWriteLog(saveTaskIdCommand.getErrorMsg());
//                duijieAsync.setTaskState(saveTaskIdCommand.getTaskState());
//                duijieAsync.setAccSet(saveTaskIdCommand.getAccSet());
//                duijieAsync.setWriteDate(new Date());
//            }
//        }
//
//
//         duijieAsyncRepository.saveAll(duijieAsyncList);

//        if (duijieAsyncDtos.size() > 0) {
//            DuijieAsync duijieAsync = duijieAsyncDtos.get(0);
//            duijieAsync.setSjjhbz(saveTaskIdCommand.getSjjhbz());
//            duijieAsync.setWriteLog(saveTaskIdCommand.getErrorMsg());
//            duijieAsync.setTaskState(saveTaskIdCommand.getTaskState());
//            duijieAsync.setAccSet(saveTaskIdCommand.getAccSet());
//            duijieAsync.setWriteDate(new Date());
//            duijieAsyncRepository.save(duijieAsync);
//        }

    }


    @Override
    public DuijieBatchDto createBatchData(CreateBatchDataCommand command) {
        // 每套账初始化

        // 总共有多少个批次
//        int batchCount = command.getBatchCount();
        String ztDataKey = command.getZtDataKey();
        // 批次标识顺序
        Map<String, String> nextBatchMap = command.getNextBatchMap();

//        List<DuijieBatch> duijieBatches = new ArrayList<>(batchCount);
        // 生成
        String batchIdPrefix = ztDataKey + "-" + System.currentTimeMillis() + "-";
        List<DuijieBatch> duijieBatches = getBatches(ztDataKey, nextBatchMap, batchIdPrefix, command.getYears(), command.getMonths(), true);
        duijieBatchRepository.saveAll(duijieBatches);

        return duijieBatchConverter.poConvertDTO(duijieBatches.get(0));
    }

    private List<DuijieBatch> getBatches(String ztDataKey, Map<String, String> nextBatchMap, String batchIdPrefix, Integer years, Integer months, boolean setFirstExecuting) {
        List<DuijieBatch> duijieBatches = new ArrayList<>(nextBatchMap.size());
        int i = 0;
        for (Map.Entry<String, String> entry : nextBatchMap.entrySet()) {
            i++;
            String currentBatchKey = entry.getKey();
            String nextBatchKey = entry.getValue();

            DuijieBatch duijieBatch = new DuijieBatch();
            duijieBatch.setZtDataKey(ztDataKey);
            duijieBatch.setBatchId(batchIdPrefix + currentBatchKey);

            // 如果是最后一批 则不用再赋值下一批次id
            if (nextBatchKey == null) {
                duijieBatch.setNextBatchId(null);
            } else {
                duijieBatch.setNextBatchId(batchIdPrefix + nextBatchKey);
            }
            duijieBatch.setBatchOrder(i);
            duijieBatch.setCreateDate(new Date());
            duijieBatch.setYears(years);
            duijieBatch.setMonths(months);
            if (i == 1 && setFirstExecuting) {
                // 将第一条设为等待中
                duijieBatch.setSjjhbz(SJJHBZ.EXECUTING.getValue());
            } else {
                duijieBatch.setSjjhbz(null);
            }

            duijieBatches.add(duijieBatch);
        }
        return duijieBatches;
    }


    @Override
    public String createOrderByMonthBatchData(CreateBatchDataCommand command) {
        // 每套账初始化

        // 总共有多少个批次
        int batchCount = command.getBatchCount();
        String ztDataKey = command.getZtDataKey();
        Integer startYears = command.getYears(), endYears = command.getEndYears();
        Integer startMonths = command.getMonths(), endMonths = command.getEndMonths();

        // 获取这个年月区间中间有哪些年月 返回一个列表
        List<YearMonth> yearMonthsBetween = DateUtil.getYearMonthsBetween(LocalDate.of(startYears, startMonths, 1), LocalDate.of(endYears, endMonths, 1));
//        List<String> yearMonths = ListUtil.toSingleList(yearMonthsBetween, yearMonth -> CommonUtil.getSelectedYearMonth(yearMonth.getYear(), yearMonth.getMonthValue()));

        // 批次标识顺序
        Map<String, String> nextBatchMap = command.getNextBatchMap();

        List<DuijieBatch> duijieBatches = new ArrayList<>(batchCount * yearMonthsBetween.size());
        // 按月份循环
        for (YearMonth yearMonth : yearMonthsBetween) {

            // 生成
            String yearMonthString = CommonUtil.getSelectedYearMonth(yearMonth.getYear(), yearMonth.getMonthValue());
            String batchIdPrefix = ztDataKey + "-" + yearMonthString + "-" + System.currentTimeMillis() + "-";
            List<DuijieBatch> batches = getBatches(ztDataKey, nextBatchMap, batchIdPrefix, yearMonth.getYear(), yearMonth.getMonthValue(), false);


            // 获取上个月最后一个批次 设置下一批次为本月
            if (duijieBatches.size() > 0) {
                DuijieBatch lastBatch = duijieBatches.get(duijieBatches.size() - 1);
                DuijieBatch thisYearMonthFirstBatch = batches.get(0);
                lastBatch.setNextBatchId(thisYearMonthFirstBatch.getBatchId());
            }

            duijieBatches.addAll(batches);
        }
        // 手动设置第一条为执行中
        duijieBatches.get(0).setSjjhbz(SJJHBZ.EXECUTING.getValue());

        duijieBatchRepository.saveAll(duijieBatches);

        return duijieBatches.get(0).getBatchId();
    }

    @Override
    public List<DuijieBatchDto> getExecutingBatches() {
        List<DuijieBatch> duijieBatches = duijieBatchRepository.getAllBySjjhbz(SJJHBZ.EXECUTING.getValue());
//        duijieBatches.addAll(duijieBatchRepository.getAllBySjjhbz(SJJHBZ.FEEDBACK_BUT_NOT_CHECK_COMPLETE.getValue()));
        return duijieBatchConverter.poConvertDTO(duijieBatches);
    }

    @Override
    public List<DuijieBatchDto> getNeedDuijieBatches() {
        List<DuijieBatch> duijieBatches = duijieBatchRepository.getAllBySjjhbz(SJJHBZ.WAITING.getValue());
        return duijieBatchConverter.poConvertDTO(duijieBatches);
    }

    @Override
    public List<DuijieAsyncDto> getCheckAsyncTasksByBatchId(BatchQuery batchQuery) {
        List<DuijieAsync> duijieAsyncs = duijieAsyncRepository.getAllByBatchId(batchQuery.getBatchId());
        return duijieAsyncConverter.poConvertDTO(duijieAsyncs);
    }

    @Override
    public void updateNextBatchDataToDuijie(String nextBatchId) {
        if (nextBatchId == null) return;
        Optional<DuijieBatch> optional = duijieBatchRepository.getByBatchId(nextBatchId);
        if (optional.isPresent()) {
            DuijieBatch duijieBatch = optional.get();
            duijieBatch.setSjjhbz(SJJHBZ.WAITING.getValue());
            duijieBatchRepository.save(duijieBatch);
        } else {
            log.debug("无法查询到下一个batchId的批次数据，更新失败！" + nextBatchId);
        }
    }

    @Override
    public void saveBatchData(List<DuijieBatchDto> batches) {
        List<DuijieBatch> duijieBatches = duijieBatchConverter.dtoConvertPO(batches);
        duijieBatchRepository.saveAll(duijieBatches);
    }

    @Override
    public void saveBatchData(DuijieBatchDto batch) {
        DuijieBatch duijieBatch = duijieBatchConverter.dtoConvertPO(batch);
        duijieBatchRepository.save(duijieBatch);
    }

    @Override
    public List<String> getDataKeysAlreadyDeleted(String serviceCode) {
        Optional<DataApi> optionalDataApi = DataApi.ofOptional(serviceCode);
        DataApi dataApi = optionalDataApi.orElseThrow(() -> new RuntimeException("该serviceCode无效！"));
        String selectSql = "select dataKey as DATA_KEY from DUIJIE_DATA_KEYS where methodNo=:methodNo and sjjhbz=0 and not exists (select 1 from "+ dataApi.getTableName() + " where data_key=DUIJIE_DATA_KEYS.dataKey)";
        Map<String, Object> params = new HashMap<>();
        params.put("methodNo", serviceCode);
        List<Map<String, Object>> maps = EntityManagerUtil.queryForMapList(entityManager, selectSql, params);
        return ListUtil.toSingleList(maps, map -> (String) map.get("DATA_KEY"));
    }

    @Override
    public List<DuijieDataKeysDto> getDuijieDataKeysLike(String serviceCode, String dataKey) {
        List<DuijieDataKeys> duijieDataKeys = duijieDataKeysRepository.getByMethodNoAndDataKeyLike(serviceCode, "%" + dataKey + "%");
        return duijieDataKeysConverter.poConvertDTO(duijieDataKeys);
    }

    @Override
    public void saveDuijieDataKeys(List<DuijieDataKeysDto> duijieDataKeysDtos) {
        List<DuijieDataKeys> duijieDataKeys = duijieDataKeysConverter.dtoConvertPO(duijieDataKeysDtos);
        duijieDataKeysRepository.saveAll(duijieDataKeys);

    }

    @Override
    public void deleteDuijieDataKeys(DeleteDataKeysCommand command) {
        String serviceCode = command.getServiceCode();
        List<String> dataKeys = command.getDataKeys();

        // v1.6.4 in 2100个参数问题
        int count = 0;
        while (count < dataKeys.size()) {

            List<String> subDataKeys = ListUtil.getSubList(dataKeys, count, count + 1000);

            List<DuijieDataKeys> needDeleteDataKeys = duijieDataKeysRepository.getByMethodNoAndDataKeyIn(serviceCode, subDataKeys);
            for (DuijieDataKeys needDeleteDataKey : needDeleteDataKeys) {
                // 打上成功标志 意为已删除
                needDeleteDataKey.setSjjhbz(SJJHBZ.SUCCESS.getValue());
                needDeleteDataKey.setWriteDate(new Date());
                needDeleteDataKey.setWriteLog("已删除");
            }
            duijieDataKeysRepository.saveAll(needDeleteDataKeys);

            count += 1000;
        }
    }

    public void saveCheckErrorResults(List<DuijieCheck> duijieChecks) {

        duijieCheckRepository.saveAll(duijieChecks);
    }

    @Override
    public List<DuijieAsyncDto> getAsyncTasksDataByWriteDate(Date checkStartDate) {
        // 处理传值是空的情况 默认是今天0时
        Date writeDateStart;
        if (checkStartDate == null) {
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            writeDateStart = cal.getTime();
        } else {
            writeDateStart = checkStartDate;
        }

        List<DuijieAsync> all = duijieAsyncRepository.findAll((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> ps = new ArrayList<>();

            ps.add(criteriaBuilder.greaterThanOrEqualTo(root.get("writeDate"), writeDateStart));

            criteriaQuery.where(ps.toArray(new Predicate[0]));
            criteriaQuery.orderBy(criteriaBuilder.asc(root.get("writeDate")));
            return criteriaQuery.getRestriction();
        });


        return duijieAsyncConverter.poConvertDTO(all);
    }

    @Override
    public <T> T getVarByKey(String varKey, Class<T> clazz) {
        Optional<DuijieVar> optional = duijieVarRepository.getByVarKey(varKey);
        if (!optional.isPresent()) {
            return null;
        }
        DuijieVar var = optional.get();

        Integer varType = var.getVarType();
        switch (varType) {

            case 2:
                return (T) var.getIntVal();

            case 3:
                return (T) var.getMoneyVal();

            case 4:
                return (T) var.getDateVal();

            case 5:
                return (T) var.getBoolVal();

            default:
                return (T) var.getStrVal();
        }
    }

    @Override
    public void saveVarByKey(String varKey, Object var) {
        Optional<DuijieVar> optional = duijieVarRepository.getByVarKey(varKey);
        DuijieVar duijieVar = optional.orElseGet(DuijieVar::new);

        duijieVar.setVarKey(varKey);
        int varType;
        if (var instanceof String) {
            varType = 1;
            duijieVar.setStrVal((String) var);
        } else if (var instanceof Integer) {
            varType = 2;
            duijieVar.setIntVal((Integer) var);
        } else if (var instanceof Double) {
            varType = 3;
            duijieVar.setMoneyVal((Double) var);
        } else if (var instanceof Date) {
            varType = 4;
            duijieVar.setDateVal((Date) var);
        } else if (var instanceof Boolean) {
            varType = 5;
            duijieVar.setBoolVal((Boolean) var);
        } else {
            varType = 1;
            duijieVar.setStrVal(var == null ? null : var.toString());
        }

        duijieVar.setVarType(varType);
        duijieVar.setWriteDate(new Date());

        duijieVarRepository.save(duijieVar);
    }

    @Override
    public DuijieProcLog createAndSaveNewDuijieProcLog(BuildWithProcCommand command) {
        ProcInfo procInfo = SystemConstants.PROC_KEY_MAP.get(command.getProcKey());
        DuijieProcLog log = new DuijieProcLog();
        log.setProcType(command.getProcKey());
        log.setProcName(procInfo.getProcName());
        log.setParams(command.getArgs().toString());
        log.setStatus(SystemConstants.PROC_RESULT_STATUS_EXECUTING);
        log.setWriteLog("执行中");
        log.setCreateDate(new Date());
        log.setWriter(command.getUserName());

        procLogRepository.save(log);
        return log;
    }

    @Override
    public void saveDuijieProcLog(DuijieProcLog log, CheckResultDto resultDto, boolean isInterrupted) {
        int status;
        if (isInterrupted) {
            status = SystemConstants.PROC_RESULT_STATUS_INTERRUPT;
        } else if (resultDto.isSuccess()) {
            status = SystemConstants.PROC_RESULT_STATUS_SUCCESS;
        } else {
            status = SystemConstants.PROC_RESULT_STATUS_FAIL;
        }
        String message = resultDto.getMessages() != null ? resultDto.getMessages().get(0) : "";

        log.setStatus(status);
        log.setWriteLog(message);
        log.setWriteDate(new Date());
        log.setEndDate(new Date());
        procLogRepository.save(log);
    }

    @Override
    public void saveDuijieProcLog(Integer id, CheckResultDto resultDto, boolean isInterrupted) {
        Optional<DuijieProcLog> optional = procLogRepository.findById(id);
        DuijieProcLog procLog = optional.orElseThrow(() -> new RuntimeException("id不存在"));
        saveDuijieProcLog(procLog, resultDto, isInterrupted);
    }

    @Override
    public PageDTO<DuijieProcLog> getProcLogs(PageQuery duijieQuery) {
        Pageable pageable = PageUtil.toPageable(duijieQuery.getCurrent() - 1, duijieQuery.getSize(), null);
        Page<DuijieProcLog> data = procLogRepository.findAll(((root, criteriaQuery, criteriaBuilder) -> {

//            List<Predicate> ps = new ArrayList<>();
//            criteriaQuery.where(ps.toArray(new Predicate[0]));

            criteriaQuery.orderBy(criteriaBuilder.desc(root.get("id")));
            return criteriaQuery.getRestriction();
        }), pageable);

        return PageDTO.of(data.getTotalElements(), data.getTotalPages(), data.getContent());
    }


}
